import { ApiResponse } from "apisauce"
import { applySnapshot, flow, Instance, SnapshotIn, SnapshotOut, types } from "mobx-state-tree"

import {
  getConversations as apiGetConversations,
  type ConversationSummary,
  type GetConversationsReq,
  type GetConversationsResp,
} from "@/services/api/conversations"

const createInitialSnapshot = () => ({
  conversations: [] as ConversationSummary[],
  conversationsLast: null as string | null,
  conversationsHasMore: false,
  isFetchingConversations: false,
  currentConversationId: null as string | null,
})

export const ConversationsModel = types
  .model("Conversations", {
    conversations: types.optional(types.array(types.frozen<ConversationSummary>()), []),
    conversationsLast: types.maybeNull(types.string),
    conversationsHasMore: types.optional(types.boolean, false),
    isFetchingConversations: types.optional(types.boolean, false),
    currentConversationId: types.maybeNull(types.string),
  })
  .views((self) => ({
    get hasConversations() {
      return self.conversations.length > 0
    },
  }))
  .actions((self) => {
    function reset() {
      applySnapshot(self, createInitialSnapshot())
    }

    function upsertConversation(summary: ConversationSummary) {
      const index = self.conversations.findIndex(
        (item) => item.conversationId === summary.conversationId,
      )
      if (index >= 0) {
        self.conversations.splice(index, 1, summary)
      } else {
        self.conversations.push(summary)
      }
    }

    function setConversations(items: ConversationSummary[], append = false) {
      if (append) {
        items.forEach(upsertConversation)
      } else {
        self.conversations.replace(items)
      }
    }
    const fetchConversations = flow(function* fetchConversations(params?: GetConversationsReq) {
      self.isFetchingConversations = true
      try {
        const response = (yield apiGetConversations(params)) as ApiResponse<GetConversationsResp>
        if (response.ok && response.data?.code === 200) {
          const { conversations, last, hasMore } = response.data.data
          setConversations(conversations, Boolean(params?.last))
          self.conversationsLast = last ?? null
          self.conversationsHasMore = Boolean(hasMore)
          return { success: true, data: response.data.data }
        }
        return { success: false, error: response.data ?? response.problem }
      } finally {
        self.isFetchingConversations = false
      }
    })

    return {
      reset,
      fetchConversations,
      upsertConversation,
      setConversations,
    }
  })

export interface Conversations extends Instance<typeof ConversationsModel> {}
export interface ConversationsSnapshotOut extends SnapshotOut<typeof ConversationsModel> {}
export interface ConversationsSnapshotIn extends SnapshotIn<typeof ConversationsModel> {}

export const createConversationsDefaultModel = () =>
  types.optional(ConversationsModel, () => createInitialSnapshot())

export const createConversationsInitialSnapshot = () => createInitialSnapshot()
